// Copyright Epic Games, Inc. All Rights Reserved.

#include "cache_cmd.h"

#include <zencore/except.h>
#include <zencore/filesystem.h>
#include <zencore/logging.h>
#include <zenhttp/httpcommon.h>
#include <zenutil/zenserverprocess.h>

#include <memory>

ZEN_THIRD_PARTY_INCLUDES_START
#include <cpr/cpr.h>
ZEN_THIRD_PARTY_INCLUDES_END

namespace zen {

DropCommand::DropCommand()
{
	m_Options.add_options()("h,help", "Print help");
	m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
	m_Options.add_option("", "n", "namespace", "Namespace name", cxxopts::value(m_NamespaceName), "<namespacename>");
	m_Options.add_option("", "b", "bucket", "Bucket name", cxxopts::value(m_BucketName), "<bucketname>");
	m_Options.parse_positional({"namespace", "bucket"});
}

DropCommand::~DropCommand() = default;

int
DropCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
	ZEN_UNUSED(GlobalOptions);

	if (!ParseOptions(argc, argv))
	{
		return 0;
	}

	m_HostName = ResolveTargetHostSpec(m_HostName);

	if (m_HostName.empty())
	{
		throw zen::OptionParseException("unable to resolve server specification");
	}

	if (m_NamespaceName.empty())
	{
		throw zen::OptionParseException("Drop command requires a namespace");
	}

	cpr::Session Session;
	if (m_BucketName.empty())
	{
		ZEN_CONSOLE("Dropping cache namespace '{}' from '{}'", m_NamespaceName, m_HostName);
		Session.SetUrl({fmt::format("{}/z$/{}", m_HostName, m_NamespaceName)});
	}
	else
	{
		ZEN_CONSOLE("Dropping cache bucket '{}/{}' from '{}'", m_NamespaceName, m_BucketName, m_HostName);
		Session.SetUrl({fmt::format("{}/z$/{}/{}", m_HostName, m_NamespaceName, m_BucketName)});
	}

	cpr::Response Result = Session.Delete();

	if (zen::IsHttpSuccessCode(Result.status_code))
	{
		ZEN_CONSOLE("OK: drop succeeded");

		return 0;
	}

	if (Result.status_code)
	{
		ZEN_ERROR("Drop failed: {}: {} ({})", Result.status_code, Result.reason, Result.text);
	}
	else
	{
		ZEN_ERROR("Drop failed: {}", Result.error.message);
	}

	return 1;
}

CacheInfoCommand::CacheInfoCommand()
{
	m_Options.add_options()("h,help", "Print help");
	m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
	m_Options.add_option("", "n", "namespace", "Namespace name", cxxopts::value(m_NamespaceName), "<namespacename>");
	m_Options.add_option("", "b", "bucket", "Bucket name", cxxopts::value(m_BucketName), "<bucketname>");
	m_Options.parse_positional({"namespace", "bucket"});
}

CacheInfoCommand::~CacheInfoCommand() = default;

int
CacheInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
	ZEN_UNUSED(GlobalOptions);

	if (!ParseOptions(argc, argv))
	{
		return 0;
	}

	m_HostName = ResolveTargetHostSpec(m_HostName);

	if (m_HostName.empty())
	{
		throw zen::OptionParseException("unable to resolve server specification");
	}

	cpr::Session Session;
	Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
	if (m_HostName.empty())
	{
		ZEN_CONSOLE("Info on cache from '{}'", m_HostName);
		Session.SetUrl({fmt::format("{}/z$", m_HostName)});
	}
	else if (m_BucketName.empty())
	{
		ZEN_CONSOLE("Info on cache namespace '{}' from '{}'", m_NamespaceName, m_HostName);
		Session.SetUrl({fmt::format("{}/z$/{}", m_HostName, m_NamespaceName)});
	}
	else
	{
		ZEN_CONSOLE("Info on cache bucket '{}/{}' from '{}'", m_NamespaceName, m_BucketName, m_HostName);
		Session.SetUrl({fmt::format("{}/z$/{}/{}", m_HostName, m_NamespaceName, m_BucketName)});
	}

	cpr::Response Result = Session.Get();

	if (zen::IsHttpSuccessCode(Result.status_code))
	{
		ZEN_CONSOLE("{}", Result.text);

		return 0;
	}

	if (Result.status_code)
	{
		ZEN_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text);
	}
	else
	{
		ZEN_ERROR("Info failed: {}", Result.error.message);
	}

	return 1;
}

CacheStatsCommand::CacheStatsCommand()
{
	m_Options.add_options()("h,help", "Print help");
	m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}

CacheStatsCommand::~CacheStatsCommand() = default;

int
CacheStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
	ZEN_UNUSED(GlobalOptions);

	if (!ParseOptions(argc, argv))
	{
		return 0;
	}

	m_HostName = ResolveTargetHostSpec(m_HostName);

	if (m_HostName.empty())
	{
		throw zen::OptionParseException("unable to resolve server specification");
	}

	cpr::Session Session;
	Session.SetUrl({fmt::format("{}/stats/z$", m_HostName)});
	Session.SetHeader(cpr::Header{{"Accept", "application/json"}});

	cpr::Response Result = Session.Get();

	if (zen::IsHttpSuccessCode(Result.status_code))
	{
		ZEN_CONSOLE("{}", Result.text);

		return 0;
	}

	if (Result.status_code)
	{
		ZEN_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text);
	}
	else
	{
		ZEN_ERROR("Info failed: {}", Result.error.message);
	}

	return 1;
}

CacheDetailsCommand::CacheDetailsCommand()
{
	m_Options.add_options()("h,help", "Print help");
	m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
	m_Options.add_option("", "c", "csv", "Info on csv format", cxxopts::value(m_CSV), "<csv>");
	m_Options.add_option("", "d", "details", "Get detailed information about records", cxxopts::value(m_Details), "<details>");
	m_Options.add_option("",
						 "a",
						 "attachmentdetails",
						 "Get detailed information about attachments",
						 cxxopts::value(m_AttachmentDetails),
						 "<attachmentdetails>");
	m_Options.add_option("", "n", "namespace", "Namespace name to get info for", cxxopts::value(m_Namespace), "<namespace>");
	m_Options.add_option("", "b", "bucket", "Filter on bucket name", cxxopts::value(m_Bucket), "<bucket>");
	m_Options.add_option("", "v", "valuekey", "Filter on value key hash string", cxxopts::value(m_ValueKey), "<valuekey>");
}

CacheDetailsCommand::~CacheDetailsCommand() = default;

int
CacheDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
	ZEN_UNUSED(GlobalOptions);

	if (!ParseOptions(argc, argv))
	{
		return 0;
	}

	m_HostName = ResolveTargetHostSpec(m_HostName);

	if (m_HostName.empty())
	{
		throw zen::OptionParseException("unable to resolve server specification");
	}

	cpr::Session	Session;
	cpr::Parameters Parameters;
	if (m_Details)
	{
		Parameters.Add({"details", "true"});
	}
	if (m_AttachmentDetails)
	{
		Parameters.Add({"attachmentdetails", "true"});
	}
	if (m_CSV)
	{
		Parameters.Add({"csv", "true"});
	}
	else
	{
		Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
	}

	if (!m_ValueKey.empty())
	{
		if (m_Namespace.empty() || m_Bucket.empty())
		{
			ZEN_ERROR("Provide namespace and bucket name");
			ZEN_CONSOLE("{}", m_Options.help({""}).c_str());
			return 1;
		}
		Session.SetUrl({fmt::format("{}/z$/details$/{}/{}/{}", m_HostName, m_Namespace, m_Bucket, m_ValueKey)});
	}
	else if (!m_Bucket.empty())
	{
		if (m_Namespace.empty())
		{
			ZEN_ERROR("Provide namespace name");
			ZEN_CONSOLE("{}", m_Options.help({""}).c_str());
			return 1;
		}
		Session.SetUrl({fmt::format("{}/z$/details$/{}/{}", m_HostName, m_Namespace, m_Bucket)});
	}
	else if (!m_Namespace.empty())
	{
		Session.SetUrl({fmt::format("{}/z$/details$/{}", m_HostName, m_Namespace)});
	}
	else
	{
		Session.SetUrl({fmt::format("{}/z$/details$", m_HostName)});
	}
	Session.SetParameters(Parameters);

	cpr::Response Result = Session.Get();

	if (zen::IsHttpSuccessCode(Result.status_code))
	{
		ZEN_CONSOLE("{}", Result.text);

		return 0;
	}

	if (Result.status_code)
	{
		ZEN_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text);
	}
	else
	{
		ZEN_ERROR("Info failed: {}", Result.error.message);
	}

	return 1;
}

}  // namespace zen
